package uk.ac.glasgow.oose2.gcf.model.tests;

import static org.junit.Assert.*;

import uk.ac.glasgow.oose2.gcf.model.CalculatorException;
import uk.ac.glasgow.oose2.gcf.model.FaultyCalculator;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.*;

/**
 * 
 * 
 *
 */

/**
 * The general testing strategy that has been implemented:
 * 	1. Divide test cases into the following  general partitions: 
 *  	+/-[0,1] covers rational numbers and 0 and 1,
 *  	+/-[1,1000] covers small integers,
 *  	+/-[1000,MAX_DOUBLE] covers large integers.
 *  
 *  2. Identification testing: Test unary operations for errors within these partitions and implement further
 *  	tests for binary operations to check for errors dependent on the order of the operands.
 *  	Tests will be implemented on boundary cases and a small number of random interior cases.
 *   
 *  3. Scope testing: If necessary, implement further tests within partitions that exhibit failure that will
 *  	attempt to find tighter	upper/lower bounds on the erroneous values. This will involve halving the
 *  	partition size and repeating 2. except for +/-[1000,MAX_DOUBLE] where it is too inefficient.
 *  
 * @author William Grayson - 1107605G
 *
 */
public class FaultyCalculatorTest
{

	private FaultyCalculator fc;

	private final double MAX_DOUBLE = Double.MAX_VALUE;
	private double [][] randCases;



	/**
	 * Helper method: generates a random positive integer in the range (min, max)
	 * 
	 * @param min
	 * @param max
	 * @return random integer of type double
	 */
	private double getRand(int min, int max)
	{
		Random rand = new Random();
		int randInt = -1;
		while(randInt <= min)
			randInt = rand.nextInt(max);

		return (double) randInt;
	}

	/**
	 *  Resets calculator and generates a new set of random numbers.
	 * @throws Exception
	 */
	@Before
	public void setUp() throws Exception
	{
		fc = new FaultyCalculator();

		//generates four random values from each partition
		randCases = new double[6][100];

		for(int  j = 0; j < 10; j++)
		{
			// +/-[0,1]
			randCases[0][j] = Math.random();
			randCases[1][j + 50] = -Math.random();
			// +/-[1,1000]
			randCases[2][j] = getRand(1, 1000);
			randCases[3][j + 50] = -getRand(1, 1000);
			//+/-[1000, MAX_DOUBLE]
			randCases[4][j] = getRand(1000, (int) Math.floor(MAX_DOUBLE));
			randCases[5][j + 50] = -getRand(1000, (int) Math.floor(MAX_DOUBLE));
		}
	}

	/**
	 * Removes existing calculator
	 * @throws Exception
	 */
	@After
	public void tearDown() throws Exception
	{
		fc = null;
		randCases = null;
	}

	/**
	 * Helper method that tests addition of two numbers a b +, implemented by FaultyCalculator by replacing top two elements
	 * of the stack with their sum.
	 * This is used in individual test cases to reduce redundant code.
	 * Test accurate to 4 d.p.
	 * 
	 * Based on the failure of testPlusOneTo1000 and testPlusNeg1000To1000 compared to the success of all
	 *  boundary tests, testPlusNegOneToNeg1000 and testPlus1000ToNeg1000, it can be assumed that:
	 *   1. The operation fails for 0 + 0
	 *   2. Otherwise the failure is only dependent on the value of the second operand
	 *   3. The operation only fails for values between 1 and 1000.
	 *   
	 *   The scoping tests for plus will take a fixed first operand 1 for consistency as testPlusOne was
	 *   successful.
	 *   
	 *  Based on the results of the scoping tests the error can be restricted as follows:
	 *   1. The operation fails for values of b such that: 100 <= b < 150, or 200 < b <= 500.
	 * 
	 */
	private void testPlus(double a, double b)
	{
		try
		{
			fc.enter(a);
			fc.enter(b);
			fc.plus();
			double expect = a + b;
			double actual = fc.top();
			double delta = 0.0001;
			fc.clear();
			assertEquals("Correct values for " + a + " + " + b ,expect, actual, delta);
		}
		catch(CalculatorException ce)
		{
			fail("Calculator exception thrown: " + a + " + " + b);
		}
	}

	/**
	 * Tests boundary case 0 + 0
	 * Failure: Expected result = 0, actual result = 0.1
	 */
	@Test
	public void testPlusZero()
	{
		testPlus(0,0);
	}

	/**
	 * Tests boundary case 1 + 1
	 * Success
	 */
	@Test
	public void testPlusOne()
	{
		testPlus(1,1);
	}

	/**
	 * Tests boundary case -1 + -1
	 * Success
	 */
	@Test
	public void testPlusNegOne()
	{
		testPlus(-1,-1);
	}

	/**
	 * Tests boundary case 0 + 1
	 * Success
	 */
	@Test
	public void testPlusZeroOne()
	{
		testPlus(0,1);
	}

	/**
	 * Tests random cases in (0,1)
	 * Success
	 */
	@Test
	public void testPlusZeroToOne() 
	{
		for(double a: randCases[0])
		{
			for(double b: randCases[0])
				if(a != 0 && b != 0)
					testPlus(a,b);
		}
	}

	/**
	 * Tests random cases in (0,1) X (-1,0)
	 * Success
	 */
	@Test
	public void testPlusNegOneToOne()
	{
		for(double a: randCases[1])
		{
			for(double b: randCases[0])
				if(a != 0 && b != 0)
					testPlus(a,b);
		}
	}


	/**
	 * Tests random cases in (-1,0) X (0,1)
	 * Success
	 */
	@Test
	public void testPlusOneToNegOne()
	{
		for(double a: randCases[0])
		{
			for(double b: randCases[1])
				if(a != 0 && b != 0)
					testPlus(a,b);
		}
	}

	/**
	 * Tests random cases in (-1,0)
	 * Success
	 */
	@Test
	public void testPlusZeroToNegOne()
	{
		for(double a: randCases[1])
		{
			for(double b: randCases[1])
				if(a != 0 && b != 0)
					testPlus(a,b);
		}
	}

	/**
	 * Tests boundary case 1000 + 1000
	 * Success
	 */
	@Test
	public void testPlus1000()
	{
		testPlus(1000,1000);
	}

	/**
	 * Tests boundary case -1000 + -1000
	 * Success
	 */
	@Test
	public void testPlusNeg1000()
	{
		testPlus(-1000,-1000);
	}
	
	/**
	 * Tests random cases in (1,1000)
	 * Failure: Case 621 + 304, Expected result = 925, actual result = -317
	 *          Case 536 + 257, Expected result = 793, actual result = -279
	 *          Case 111 + 111, Expected result = 222, actual result = 0
	 *          
	 * These results imply that a + b is calculated as -a + b
	 */
	@Test
	public void testPlusOneTo1000() 
	{
		for(double a: randCases[2])
		{
			for(double b: randCases[2])
				if(a != 0 && b != 0)
					testPlus(a,b);		}
	}

	/**
	 * Tests random cases in (-1000,-1) X (1,1000)
	 * Failure: Case -705 + 453, Expected result = -252, actual result = 115
	 *          Case -737 + 108, Expected result = -629, actual result = 845
	 *          
	 * These results imply that a + b is calculated as -a + b
	 */
	@Test
	public void testPlusNeg1000To1000() 
	{
		for(double a: randCases[3])
		{
			for(double b: randCases[2])
				if(a != 0 && b != 0)
					testPlus(a,b);
		}
	}

	/**
	 * Tests random cases in (1,1000) X (-1000,-1)
	 * Success
	 */
	@Test
	public void testPlus1000ToNeg1000() 
	{
		for(double a: randCases[2])
		{
			for(double b: randCases[3])
				if(a != 0 && b != 0)
					testPlus(a,b);
		}
	}

	/**
	 * Tests random cases in (-1000,-1)
	 * Success
	 */
	@Test
	public void testPlusNegOneToNeg1000() 
	{
		for(double a: randCases[3])
		{
			for(double b: randCases[3])
				if(a != 0 && b != 0)
					testPlus(a,b);
		}
	}

	/**
	 * Tests boundary case MAX_DOUBLE + MAX_DOUBLE
	 * Success
	 */
	@Test
	public void testPlusMax()
	{
		testPlus(MAX_DOUBLE,MAX_DOUBLE);
	}

	/**
	 * Tests boundary case -MAX_DOUBLE + -MAX_DOUBLE
	 * Success
	 */
	@Test
	public void testPlusNegMax()
	{
		testPlus(-MAX_DOUBLE,-MAX_DOUBLE);
	}

	/**
	 * Tests random cases in (1000,MAX_DOUBLE)
	 * Success
	 */
	@Test
	public void testPlus1000ToMax() 
	{
		for(double a: randCases[4])
		{
			for(double b: randCases[4])
				if(a != 0 && b != 0)
					testPlus(a,b);
		}
	}

	/**
	 * Tests random cases in (-MAX_DOUBLE,-1000) X (1000,MAX_DOUBLE)
	 * Success
	 */
	@Test
	public void testPlusNeg1000ToMax() 
	{
		for(double a: randCases[5])
		{
			for(double b: randCases[4])
				if(a != 0 && b != 0)
					testPlus(a,b);
		}
	}

	/**
	 * Tests random cases in (1000,MAX_DOUBLE) X (-MAX_DOUBLE,-1000)
	 * Success
	 */
	@Test
	public void testPlus1000ToNegMax() 
	{
		for(double a: randCases[4])
		{
			for(double b: randCases[5])
				if(a != 0 && b != 0)
					testPlus(a,b);
		}
	}

	/**
	 * Tests random cases in (-MAX_DOUBLE,-1000)
	 * Success
	 */
	@Test
	public void testPlusNeg1000ToNegMax() 
	{
		for(double a: randCases[5])
		{
			for(double b: randCases[5])
				if(a != 0 && b != 0)
					testPlus(a,b);
		}
	}
	
	/**  
	 * Tests for b = 500
	 * Failure: Expected result = 501, actual result = 499
	 */
	@Test
	public void testPlusScope500()
	{
		testPlus(1, 500);
	}
	
	
	/**
	 * Tests for b in (1, 500)
	 * Failure: Case 1 + 315, Expected result = 316, actual result = 314
	 */
	@Test
	public void testPlusScopeOneTo500()
	{
		for(double b: randCases[2])
			if(b > 1 && b <500)
				testPlus(1, b);
	}
	
	/**
	 * Tests for b in (500, 1000)
	 * Success
	 */
	@Test
	public void testPlusScope500To1000()
	{
		for(double b: randCases[2])
			if(b > 500 && b <1000)
			{
				testPlus(1, b);
			}
	}
	
	/**
	 * Tests for b = 250
	 * Failure: Expected result = 251, actual result = 249
	 */
	@Test
	public void testPlusScope250()
	{
		testPlus(1, 250);
	}
	
	/**
	 * Tests for b in (1, 250)
	 * Success: 2, 59
	 * Failure: Case 1 + 139, Expected result = 140, actual result = 138
	 * 
	 * Contains boundary value(s) since multiple tests produce success and failure.
	 */
	@Test
	public void testPlusScopeOneTo250()
	{
		for(double b: randCases[2])
			if(b > 1 && b <250)
			{
				testPlus(1, b);
			}
	}
	
	/**
	 * Tests for b in (250, 500)
	 * Failure: Case 1 + 414, Expected result = 415, actual result = 413
	 */
	@Test
	public void testPlusScope250To500()
	{
		for(double b: randCases[2])
			if(b > 250 && b <500)
				testPlus(1, b);
	}
	
	/**
	 * Tests for b = 125
	 * Failure: Expected result = 126, actual result = 124
	 */
	@Test
	public void testPlusScope125()
	{
		testPlus(1, 125);
	}
	
	/**
	 * Tests for b in (1, 250)
	 * Success: 29, 79, 92
	 * Failure: Case 1 + 118, Expected result = 119, actual result = 117
	 * 
	 * Contains boundary values, probably between 92 and 118
	 */
	@Test
	public void testPlusScopeOneTo125()
	{
		for(double b: randCases[2])
			if(b > 1 && b <125)
			{
				testPlus(1, b);
			}
	}
	
	/**
	 * Tests for b in (1, 250)
	 * Success: 155
	 * Failure: Case 1 + 236, Expected result = 237, actual result = 235
	 * 
	 * Contains boundary value(s) since multiple tests produce success and failure.
	 */
	@Test
	public void testPlusScope125To250()
	{
		for(double b: randCases[2])
			if(b > 125 && b <250)
			{
				testPlus(1, b);
			}
	}
	
	/**
	 * Tests for b = 100
	 * Failure: Expected result = 101, actual result = 99
	 */
	@Test
	public void testPlusScope100()
	{
		testPlus(1, 100);
	}
	
	/**
	 * Tests for b = 150
	 * Success
	 */
	@Test
	public void testPlusScope150()
	{
		testPlus(1, 150);
	}
	
	/**
	 * Tests for b = 200
	 * Success
	 */
	@Test
	public void testPlusScope200()
	{
		testPlus(1, 200);
	}
	
	/**
	 * Tests for b in (150, 200)
	 * Success
	 */
	@Test
	public void testPlusScope150To200()
	{
		for(double b: randCases[2])
			if(b > 150 && b <200)
			{
				testPlus(1, b);
				System.out.println(b);
			}
	}

	/**
	 * Helper method that tests subtraction of two numbers a b -, implemented by FaultyCalculator by replacing top two elements
	 * of the stack with their difference.
	 * This is used in individual test cases to reduce redundant code.
	 * Test accurate to 4 d.p.
	 * 
	 * Based on the results of the identification tests the operation fails in the following cases
	 *   1. a < 0 AND b > 0, result is a + b
	 *   2. a > 0 AND b < 0, result is -a + b
	 *   3. a < 0 AND b < 0, result is 0
	 *  
	 *   
	 *  Since the results of the tests were consistent for all sub-partitions, e.g. (0,1) (-1000,-1) etc
	 *  and boundary tests testMinusNeg1000, testMinusNegMax can be treated as false positives (as a - a = 0)
	 *  no scope testing is necessary.
	 */
	private void testMinus(double a, double b)
	{
		try
		{
			fc.enter(a);
			fc.enter(b);
			fc.minus();

			double expect = a - b;
			double actual = fc.top();
			double delta = 0.0001;

			fc.clear();

			assertEquals("Correct values for " + a + " - " + b ,expect, actual, delta);
		}
		catch(CalculatorException ce)
		{
			fail("Calculator exception thrown: " + a + " - " + b);
		}
	}


	/**
	 * Tests boundary case 0 - 0
	 */
	@Test
	public void testMinusZero()
	{
		testMinus(0,0);
	}

	/**
	 * Tests boundary case 1 - 1
	 */
	@Test
	public void testMinusOne()
	{
		testMinus(1,1);
	}

	/**
	 * Tests boundary case -1 - -1
	 */
	@Test
	public void testMinusNegOne()
	{
		testMinus(-1,-1);
	}

	/**
	 * Tests boundary case 0 - 1
	 */
	@Test
	public void testMinusZeroOne()
	{
		testMinus(0,1);
	}

	/**
	 * Tests boundary case 1 - 0
	 */
	@Test
	public void testMinusOneZero()
	{
		testMinus(1,0);
	}

	/**
	 * Tests random cases in (0,1)
	 */
	@Test
	public void testMinusZeroToOne()
	{
		for(double a: randCases[0])
		{
			for(double b: randCases[0])
				if(a != 0 && b != 0)
					testMinus(a,b);
		}
	}

	/**
	 * Tests random cases in (0,1) X (-1,0)
	 * Failure: Case -0.48.. - 0.67.., Expected result = -1.15.., actual result = 0.19..
	 * 
	 * These results imply that a - b is calculated as a + b
	 */
	@Test
	public void testMinusNegOneToOne()
	{
		for(double a: randCases[1])
		{
			for(double b: randCases[0])
				if(a != 0 && b != 0)
					testMinus(a,b);
		}
	}


	/**
	 * Tests random cases in (-1,0) X (0,1)
	 * Failure: Case 0.97.. - -0.59.., Expected result = 1.57.., actual result = -0.59..
	 * 
	 * these results imply that a - b is calculated as -a + b
	 */
	@Test
	public void testMinusOneToNegOne()
	{
		for(double a: randCases[0])
		{
			for(double b: randCases[1])
				if(a != 0 && b != 0)
					testMinus(a,b);
		}
	}

	/**
	 * Tests random cases in (-1,0)
	 * Failure: Case -0.69.. - -0.64.., Expected result = -0.04.., actual result = 0.0..
	 * 
	 * a - b seems to consistently equal 0
	 */
	@Test
	public void testMinusZeroToNegOne()
	{
		for(double a: randCases[1])
		{
			for(double b: randCases[1])
				if(a != 0 && b != 0)
					testMinus(a,b);
		}
	}

	/**
	 * Tests boundary case 1000 - 1000
	 */
	@Test
	public void testMinus1000()
	{
		testMinus(1000,1000);
	}

	/**
	 * Tests boundary case -1000 - -1000
	 */
	@Test
	public void testMinusNeg1000()
	{
		testMinus(-1000,-1000);
	}

	/**
	 * Tests random cases in (1,1000)
	 */
	@Test
	public void testMinusOneTo1000() 
	{
		for(double a: randCases[2])
		{
			for(double b: randCases[2])
				if(a != 0 && b != 0)
					testMinus(a,b);
		}
	}
	
	/**
	 * Tests random cases in (-1000,1) X (1,1000)
	 * Failure: Case -512 - 245, Expected result = -757, actual result = -267
	 * 
	 * these results imply that a - b is calculated as a + b
	 */
	@Test
	public void testMinusNeg1000To1000() 
	{
		for(double a: randCases[3])
		{
			for(double b: randCases[2])
				if(a != 0 && b != 0)
					testMinus(a,b);
		}
	}

	/**
	 * Tests random cases in (1,1000) X (-1000,1)
	 * Failure: Case 782 - -745, Expected result = 1527, actual result = -1527
	 * 
	 * these results imply that a - b is calculated as -a + b
	 */
	@Test
	public void testMinus1000To1000() 
	{
		for(double a: randCases[2])
		{
			for(double b: randCases[3])
				if(a != 0 && b != 0)
					testMinus(a,b);
		}
	}

	/**
	 * Tests random cases in (-1000,-1)
	 * Failure: Case -491 - -197, Expected result = -294, actual result = 0.0
	 * 
	 *  a - b seems to consistently equal 0
	 */
	@Test
	public void testMinusNegOneToNeg1000() 
	{
		for(double a: randCases[3])
		{
			for(double b: randCases[3])
				if(a != 0 && b != 0)
					testMinus(a,b);
		}
	}

	/**
	 * Tests boundary case MAX_DOUBLE - MAX_DOUBLE
	 */
	@Test
	public void testMinusMax()
	{
		testMinus(MAX_DOUBLE,MAX_DOUBLE);
	}

	/**
	 * Tests boundary case -MAX_DOUBLE - -MAX_DOUBLE
	 */
	@Test
	public void testMinusNegMax()
	{
		testMinus(-MAX_DOUBLE,-MAX_DOUBLE);
	}

	/**
	 * Tests random cases in (1000,MAX_DOUBLE)
	 */
	@Test
	public void testMinus1000ToMax() 
	{
		for(double a: randCases[4])
		{
			for(double b: randCases[4])
				if(a != 0 && b != 0)
					testMinus(a,b);
		}
	}

	/**
	 * Tests random cases in (-MAX_DOUBLE,-1000) X (1000,MAX_DOUBLE)
	 * Failure: Case -4.97E8 - 1.77E9, Expected result = -2.27E9, actual result = 1.27E9
	 * 
	 * these results imply that a - b is calculated as a + b
	 */
	@Test
	public void testMinusNeg1000ToMax() 
	{
		for(double a: randCases[5])
		{
			for(double b: randCases[4])
				if(a != 0 && b != 0)
					testMinus(a,b);
		}
	}

	/**
	 * Tests random cases in (1000,MAX_DOUBLE) X (-MAX_DOUBLE,-1000)
	 * Failure: Case 1.05E9 - -1.29E9, Expected result = 2.34E9, actual result = -2.34E9
	 * 
	 * these results imply that a - b is calculated as -a + b
	 */
	@Test
	public void testMinus1000ToNegMax() 
	{
		for(double a: randCases[4])
		{
			for(double b: randCases[5])
				if(a != 0 && b != 0)
					testMinus(a,b);
		}
	}

	/**
	 * Tests random cases in (-MAX_DOUBLE,-1000)
	 * Failure: Case -2.93E8 - -1.06E9, Expected result = 7.76E8, actual result = 0.0
	 * 
	 *  a - b seems to consistently equal 0
	 */
	@Test
	public void testMinusNeg1000ToNegMax() 
	{
		for(double a: randCases[5])
		{
			for(double b: randCases[5])
				if(a != 0 && b != 0)
					testMinus(a,b);
		}
	}

	/**
	 * Helper method that tests division of two numbers a b /, implemented by FaultyCalculator by replacing top two elements
	 * of the stack with their ratio.
	 * This is used in individual test cases to reduce redundant code.
	 * Test accurate to 4 d.p.
	 * 
	 * Based on the results of the identification tests the operation fails in the following case
	 *   1. b < 0, incorrect Calculator Exception is thrown
	 *  
	 *  Since the results of the tests were consistent for all sub-partitions, e.g. (0,1) (-1000,-1) etc
	 *  no scope testing is necessary.
	 */
	private void testDivide(double a, double b)
	{
		try
		{
			fc.enter(a);
			fc.enter(b);
			fc.divide();
			double expect = a / b;
			double actual = fc.top();
			double delta = 0.0001;
			fc.clear();
			assertEquals("Correct values for " + a + " / " + b ,expect, actual, delta);
		}
		catch(CalculatorException ce)
		{
			//condition needed to prevent test failure when division by 0 throws a calculator exception.
			if ( b != 0)
				fail("Calculator exception thrown: " + a + " / " + b );
		}
	}

	/**
	 * Tests boundary case 0 / 0
	 */
	@Test
	public void testDivideZero()
	{
		testDivide(0,0);
	}



	/**
	 * Tests boundary case 1 / 1
	 */
	@Test
	public void testDivideOne()
	{
		testDivide(1,1);
	}

	/**
	 * Tests boundary case -1 / -1
	 */
	@Test
	public void testDivideNegOne()
	{
		testDivide(-1,-1);
	}

	/**
	 * Tests boundary case 0 / 1
	 */
	@Test
	public void testDivideZeroOne()
	{
		testDivide(0,1);
	}

	/**
	 * Tests random cases in (0,1)
	 */
	@Test
	public void testDivideZeroToOne() 
	{
		for(double a: randCases[0])
		{
			for(double b: randCases[0])
				if(a != 0 && b != 0)
					testDivide(a,b);
		}
	}

	/**
	 * Tests random cases in (0,1) X (-1,0)
	 */
	@Test
	public void testDivideNegOneToOne()
	{
		for(double a: randCases[1])
		{
			for(double b: randCases[0])
				if(a != 0 && b != 0)
					testDivide(a,b);
		}
	}


	/**
	 * Tests random cases in (-1,0) X (0,1)
	 * Failure: CalculatorException for 0.94../ -0.51..
	 */
	@Test
	public void testDivideOneToNegOne()
	{
		for(double a: randCases[0])
		{
			for(double b: randCases[1])
				if(a != 0 && b != 0)
					testDivide(a,b);
		}
	}

	/**
	 * Tests random cases in (-1,0)
	 * Failure: CalculatorException for -0.83../ -0.83..
	 */
	@Test
	public void testDivideZeroToNegOne()
	{
		for(double a: randCases[1])
		{
			for(double b: randCases[1])
				if(a != 0 && b != 0)
					testDivide(a,b);
		}
	}

	/**
	 * Tests random cases in (1,1000)
	 */
	@Test
	public void testDivideOneTo1000() 
	{
		for(double a: randCases[2])
		{
			for(double b: randCases[2])
				if(a != 0 && b != 0)
					testDivide(a,b);		
		}
	}

	/**
	 * Tests random cases in (1,1000)
	 */
	@Test
	public void testDivideNeg1000To1000() 
	{
		for(double a: randCases[3])
		{
			for(double b: randCases[2])
				if(a != 0 && b != 0)
					testDivide(a,b);
		}
	}

	/**
	 * Tests random cases in (1,1000)
	 * Failure: CalculatorException for 236 / -202
	 */
	@Test
	public void testDivide1000ToNeg1000() 
	{
		for(double a: randCases[2])
		{
			for(double b: randCases[3])
				if(a != 0 && b != 0)
					testDivide(a,b);
		}
	}

	/**
	 * Tests random cases in (-1000,-1)
	 * Failure: CalculatorException for -965 / -965
	 */
	@Test
	public void testDivideNegOneToNeg1000() 
	{
		for(double a: randCases[3])
		{
			for(double b: randCases[3])
				if(a != 0 && b != 0)
					testDivide(a,b);
		}
	}

	/**
	 * Tests boundary case 1000 / 1000
	 */
	@Test
	public void testDivide1000()
	{
		testDivide(1000,1000);
	}

	/**
	 * Tests boundary case -1000 / -1000
	 */
	@Test
	public void testDivideNeg1000()
	{
		testDivide(-1000,-1000);
	}

	/**
	 * Tests random cases in (1000,MAX_DOUBLE)
	 */
	@Test
	public void testDivide1000ToMax() 
	{
		for(double a: randCases[4])
		{
			for(double b: randCases[4])
				if(a != 0 && b != 0)
					testDivide(a,b);
		}
	}

	/**
	 * Tests random cases in (-MAX_DOUBLE,-1000) X (1000,MAX_DOUBLE)
	 */
	@Test
	public void testDivideNeg1000ToMax() 
	{
		for(double a: randCases[5])
		{
			for(double b: randCases[4])
				if(a != 0 && b != 0)
					testDivide(a,b);
		}
	}

	/**
	 * Tests random cases in (1000,MAX_DOUBLE) X (-MAX_DOUBLE,-1000)
	 * Failure: CalculatorException for 5.4E8 / -7.9E8
	 */
	@Test
	public void testDivide1000ToNegMax()
	{
		for(double a: randCases[4])
		{
			for(double b: randCases[5])
				if(a != 0 && b != 0)
					testDivide(a,b);
		}
	}

	/**
	 * Tests random cases in (-MAX_DOUBLE,-1000)
	 * Failure: CalculatorException for -4.61E8 / -4.61E8
	 */
	@Test
	public void testDivideNeg1000ToMaxMax() 
	{
		for(double a: randCases[5])
		{
			for(double b: randCases[5])
				if(a != 0 && b != 0)
					testDivide(a,b);
		}
	}

	/**
	 * Tests boundary case MAX_DOUBLE / MAX_DOUBLE
	 */
	@Test
	public void testDivideMax()
	{
		testDivide(MAX_DOUBLE,MAX_DOUBLE);
	}

	/**
	 * Tests boundary case -MAX_DOUBLE / -MAX_DOUBLE
	 * Failure: CalculatorException
	 */
	@Test
	public void testDivideNegMax()
	{
		testDivide(-MAX_DOUBLE,-MAX_DOUBLE);
	}

	/**
	 * Helper method that tests multiplication of two numbers a b *, implemented by FaultyCalculator by replacing top two elements
	 * of the stack with their product.
	 * This is used in individual test cases to reduce redundant code.
	 * Test accurate to 4 d.p.
	 * 
	 * Based on the results of the identification tests the operation fails in the following case
	 *   1. b > 0, result = 0
	 *  
	 *  Since the results of the tests were consistent for all sub-partitions, e.g. (0,1) (-1000,-1) etc
	 *  no scope testing is necessary.
	 */
	private void testTimes(double a, double b)
	{
		try
		{
			fc.enter(a);
			fc.enter(b);
			fc.times();
			double expect = a * b;
			double actual = fc.top();
			double delta = 0.0001;
			fc.clear();
			assertEquals("Correct values for " + a + " * " + b ,expect, actual, delta);
		}
		catch(CalculatorException ce)
		{
			fail("Calculator exception thrown: " + a + " * " + b);
		}
	}

	/**
	 * Tests boundary case 0 * 0
	 */
	@Test
	public void testTimesZero()
	{
		testTimes(0,0);
	}



	/**
	 * Tests boundary case 1 * 1
	 * Failure: Expected result = 1, actual result = 0
	 */
	@Test
	public void testTimesOne()
	{
		testTimes(1,1);
	}

	/**
	 * Tests boundary case -1 * -1
	 */
	@Test
	public void testTimesNegOne()
	{
		testTimes(-1,-1);
	}

	/**
	 * Tests boundary case 0 * 1
	 */
	@Test
	public void testTimesZeroOne()
	{
		testTimes(0,1);
	}

	/**
	 * Tests random cases in (0,1)
	 * Failure: Case 0.86.. * 0.86.., Expected result = 0.74.., actual result = 0
	 */
	@Test
	public void testTimesZeroToOne() 
	{
		for(double a: randCases[0])
		{
			for(double b: randCases[0])
				if(a != 0 && b != 0)
					testTimes(a,b);
		}
	}

	/**
	 * Tests random cases in (-1,0) X (0,1)
	 */
	@Test
	public void testTimesNegOneToOne()
	{
		for(double a: randCases[1])
		{
			for(double b: randCases[0])
				if(a != 0 && b != 0)
					testTimes(a,b);
		}
	}


	/**
	 * Tests random cases in (0,1) X (-1,0)
	 */
	@Test
	public void testTimesOneToNegOne()
	{
		for(double a: randCases[0])
		{
			for(double b: randCases[1])
				if(a != 0 && b != 0)
					testTimes(a,b);
		}
	}

	/**
	 * Tests random cases in (-1,0)
	 */
	@Test
	public void testTimesZeroToNegOne()
	{
		for(double a: randCases[1])
		{
			for(double b: randCases[1])
				if(a != 0 && b != 0)
					testTimes(a,b);
		}
	}

	/**
	 * Tests boundary case 1000 * 1000
	 * Failure: Expected result = 1000000, actual result = 0
	 */
	@Test
	public void testTimes1000()
	{
		testTimes(1000,1000);
	}

	/**
	 * Tests boundary case -1000 * -1000
	 */
	@Test
	public void testTimesNeg1000()
	{
		testTimes(-1000,-1000);
	}

	/**
	 * Tests random cases in (1,1000)
	 * Failure: Case 425 * 425, Expected result = 180625, actual result = 0
	 */
	@Test
	public void testTimesOneTo1000() 
	{
		for(double a: randCases[2])
		{
			for(double b: randCases[2])
				if(a != 0 && b != 0)
					testTimes(a,b);
		}
	}

	/**
	 * Tests random cases in (1,1000)
	 */
	@Test
	public void testTimesNeg1000To1000() 
	{
		for(double a: randCases[3])
		{
			for(double b: randCases[2])
				if(a != 0 && b != 0)
					testTimes(a,b);
		}
	}

	/**
	 * Tests random cases in (1,1000)
	 */
	@Test
	public void testTimes1000ToNeg1000() 
	{
		for(double a: randCases[2])
		{
			for(double b: randCases[3])
				if(a != 0 && b != 0)
					testTimes(a,b);
		}
	}

	/**
	 * Tests random cases in (-1000,-1)
	 */
	@Test
	public void testTimesNegOneToNeg1000() 
	{
		for(double a: randCases[3])
		{
			for(double b: randCases[3])
				if(a != 0 && b != 0)
					testTimes(a,b);
		}
	}

	/**
	 * Tests boundary case MAX_DOUBLE * MAX_DOUBLE
	 * Failure: Expected result = Infinity, actual result = 0
	 */
	@Test
	public void testTimesMax()
	{
		testTimes(MAX_DOUBLE,MAX_DOUBLE);
	}

	/**
	 * Tests boundary case -MAX_DOUBLE * -MAX_DOUBLE
	 */
	@Test
	public void testTimesNegMax()
	{
		testTimes(-MAX_DOUBLE,-MAX_DOUBLE);
	}

	/**
	 * Tests random cases in (1000,MAX_DOUBLE)
	 * Failure: Case 2.07E9 * 2.07E9, Expected result = 4.28E18, actual result = 0
	 */
	@Test
	public void testTimes1000ToMax() 
	{
		for(double a: randCases[4])
		{
			for(double b: randCases[4])
				if(a != 0 && b != 0)
					testTimes(a,b);
		}
	}

	/**
	 * Tests random cases in (-MAX_DOUBLE,-1000) X (1000,MAX_DOUBLE)
	 * Failure: Case -1.36E9 * -1.36E9, Expected result = -1.5E18, actual result = 0
	 */
	@Test
	public void testTimesNeg1000ToMax() 
	{
		for(double a: randCases[5])
		{
			for(double b: randCases[4])
				if(a != 0 && b != 0)
					testTimes(a,b);
		}
	}

	/**
	 * Tests random cases in (1000,MAX_DOUBLE) X (-MAX_DOUBLE,-1000)
	 */
	@Test
	public void testTimes1000ToNegMax() 
	{
		for(double a: randCases[4])
		{
			for(double b: randCases[5])
				if(a != 0 && b != 0)
					testTimes(a,b);
		}
	}

	/**
	 * Tests random cases in (-MAX_DOUBLE,-1000)
	 */
	@Test
	public void testTimesNeg1000ToNegMax() 
	{
		for(double a: randCases[5])
		{
			for(double b: randCases[5])
				if(a != 0 && b != 0)
					testTimes(a,b);
		}
	}

	/**
	 * Helper method that tests sign change of a number "a Sign", implemented by FaultyCalculator by replacing the top element
	 * of the stack with its additive inverse.
	 * This is used in individual test cases to reduce redundant code.
	 * Test accurate to 4 d.p.
	 * 
	 * Based on the results of the identification tests the operation fails in the following case
	 *   1. a > 0, result = a
	 *  
	 *  Since the results of the tests were consistent for all sub-partitions, e.g. (0,1) (-1000,-1) etc
	 *  no scope testing is necessary.
	 */
	private void testSign(double a)
	{
		try
		{
			fc.enter(a);
			fc.sign();
			double expect = -1 * a;
			double actual = fc.top();
			double delta = 0.0001;
			fc.clear();
			assertEquals("Correct values for -(" + a + ")",expect, actual, delta);
		}
		catch(CalculatorException ce)
		{
			fail("Calculator exception thrown: -(" + a + ")");
		}
	}

	/**
	 * Tests boundary case -(0)
	 */
	@Test
	public void testSignZero()
	{
		testSign(0);
	}

	/**
	 * Tests boundary case -(-1)
	 */
	@Test
	public void testSignNegOne()
	{
		testSign(-1);
	}

	/**
	 * Tests boundary case -(1)
	 * Failure: Expected result = -1, actual result = 1
	 */
	@Test
	public void testSignOne()
	{
		testSign(1);
	}


	/**
	 * Tests random cases in (0,1)
	 * Failure: Case 0.026.., Expected result = -0.026.., actual result = 0.026..
	 */
	@Test
	public void testSignZeroToOne() 
	{
		for(double a: randCases[0])
		{
			testSign(a);
		}
	}

	/**
	 * Tests random cases in (-1,0)
	 */
	@Test
	public void testSignZeroToNegOne()
	{
		for(double a: randCases[1])
		{
			testSign(a);
		}
	}

	/**
	 * Tests boundary case -(1000)
	 * Failure: Expected result = -1000, actual result = 1000
	 */
	@Test
	public void testSign1000()
	{
		testSign(1000);
	}

	/**
	 * Tests boundary case -(-1000)
	 */
	@Test
	public void testSignNeg1000()
	{
		testSign(-1000);
	}

	/**
	 * Tests random cases in (1,1000)
	 * Failure: Case 935, Expected result = -935, actual result = 935
	 */
	@Test
	public void testSignOneTo1000() 
	{
		for(double a: randCases[2])
		{
			testSign(a);
		}
	}

	/**
	 * Tests random cases in (-1000,-1)
	 */
	@Test
	public void testSignNegOneToNeg1000() 
	{
		for(double a: randCases[3])
		{
			testSign(a);
		}
	}

	/**
	 * Tests boundary case -(MAX_DOUBLE)
	 * Failure: Expected result = -MAX_DOUBLE, actual result = MAX_DOUBLE
	 */
	@Test
	public void testSignMax()
	{
		testSign(MAX_DOUBLE);
	}

	/**
	 * Tests boundary case -(-MAX_DOUBLE)
	 */
	@Test
	public void testSignNegMax()
	{
		testSign(-MAX_DOUBLE);
	}


	/**
	 * Tests random cases in (1000,MAX_DOUBLE)
	 * Failure: Case 7.66E8, Expected result = -7.66E8, actual result = 7.66E8
	 */
	@Test
	public void testSign1000ToMax() 
	{
		for(double a: randCases[4])
		{
			testSign(a);
		}
	}

	/**
	 * Tests random cases in (-MAX_DOUBLE,-1000)
	 */
	@Test
	public void testSignNeg1000ToNegMax() 
	{
		for(double a: randCases[5])
		{
			testSign(a);
		}
	}

	/**
	 * Helper method that tests inversion of a number "a Invert", implemented by FaultyCalculator by replacing the top element
	 * of the stack with its multiplicative inverse.
	 * This is used in individual test cases to reduce redundant code.
	 * Test accurate to 4 d.p.
	 * 
	 * Based on the results of the identification tests the operation does not fail
	 */
	private void testInvert(double a)
	{
		try
		{
			fc.enter(a);
			fc.invert();
			double expect = 1 / a;
			double actual = fc.top();
			double delta = 0.0001;
			fc.clear();
			assertEquals("Correct values for 1/" + a, expect, actual, delta);
			System.out.println(a);
		}
		catch(CalculatorException ce)
		{
			if(a != 0)
				fail("Calculator exception thrown:" + a);
		}
	}

	/**
	 * Tests boundary case 1/0
	 */
	@Test
	public void testInvertZero()
	{
		testInvert(0);
	}

	/**
	 * Tests boundary case 1/-1
	 */
	@Test
	public void testInvertNegOne()
	{
		testInvert(-1);
	}

	/**
	 * Tests boundary case 1/1
	 */
	@Test
	public void testInvertZeroOne()
	{
		testInvert(1);
	}

	/**
	 * Tests random cases in (0,1)
	 */
	@Test
	public void testInvertZeroToOne() 
	{
		for(double a: randCases[0])
		{
			testInvert(a);
		}
	}

	/**
	 * Tests random cases in (-1,0)
	 */
	@Test
	public void testInvertZeroToNegOne()
	{
		for(double a: randCases[1])
		{
			testInvert(a);
		}
	}

	/**
	 * Tests boundary case 1/1000
	 */
	@Test
	public void testInvert1000()
	{
		testInvert(1000);
	}

	/**
	 * Tests boundary case 1/-1000
	 */
	@Test
	public void testInvertNeg1000()
	{
		testInvert(-1000);
	}

	/**
	 * Tests random cases in (1,1000)
	 */
	@Test
	public void testInvertOneTo1000() 
	{
		for(double a: randCases[2])
		{
			testInvert(a);
		}
	}

	/**
	 * Tests random cases in (-1000,-1)
	 */
	@Test
	public void testInvertNegOneToNeg1000() 
	{
		for(double a: randCases[3])
		{
			testInvert(a);
		}
	}

	/**
	 * Tests boundary case 1/MAX_DOUBLE
	 */
	@Test
	public void testInvertMax()
	{
		testInvert(MAX_DOUBLE);
	}

	/**
	 * Tests boundary case 1/-MAX_DOUBLE
	 */
	@Test
	public void testInvertNegMax()
	{
		testInvert(-MAX_DOUBLE);
	}

	/**
	 * Tests random cases in (1000,MAX_DOUBLE)
	 */
	@Test
	public void testInvert1000ToMax() 
	{
		for(double a: randCases[4])
		{
			testInvert(a);
		}
	}

	/**
	 * Tests random cases in (-MAX_DOUBLE,-1000)
	 */
	@Test
	public void testInvertNeg1000ToNegMax() 
	{
		for(double a: randCases[5])
		{
			testInvert(a);
		}
	}

	/**
	 * Helper method that tests square root calculation of a number "a Sqrt", implemented by FaultyCalculator by replacing the top element
	 * of the stack with its principal square root.
	 * This is used in individual test cases to reduce redundant code.
	 * Test accurate to 4 d.p.
	 * 
	 * Based on the results of the identification tests the operation fails in the following case
	 *   1. a < 0, result = sqrt(-a), expected CalculatorException
	 *  
	 *  Since the results of the tests were consistent for all sub-partitions, e.g. (0,1) (-1000,-1) etc
	 *  no scope testing is necessary.
	 */
	private void testSqrt(double a)
	{
		try
		{
			fc.enter(a);
			fc.sqrt();
			double expect = Math.sqrt(a);
			double actual = fc.top();
			double delta = 0.0001;
			fc.clear();
			assertEquals("Correct values for sqrt(" + a + ")", expect, actual, delta);
		}
		catch(CalculatorException ce)
		{
			//The calculator should throw an exception for a negative operand
			if( a >= 0)
				fail("Calculator exception thrown: sqrt(" + a + ")");
		}
	}

	/**
	 * Tests boundary case sqrt(0)
	 */
	@Test
	public void testSqrtZero()
	{
		testSqrt(0);
	}

	/**
	 * Tests boundary case sqrt(-1)
	 * Failure: Expected result = <NaN>, actual result = 1 
	 */
	@Test
	public void testSqrtNegOne()
	{
		testSqrt(-1);
	}

	/**
	 * Tests boundary case sqrt(1)
	 */
	@Test
	public void testSqrtZeroOne()
	{
		testSqrt(1);
	}

	/**
	 * Tests random cases in (0,1)
	 */
	@Test
	public void testSqrtZeroToOne() 
	{
		for(double a: randCases[0])
		{
			testSqrt(a);
		}
	}

	/**
	 * Tests random cases in (-1,0)
	 * Failure: Case -0.206.., Expected result = <NaN>, actual result = 0.45.. 
	 */
	@Test
	public void testSqrtZeroToNegOne()
	{
		for(double a: randCases[1])
		{
			testSqrt(a);
		}
	}

	/**
	 * Tests boundary case sqrt(1000)
	 */
	@Test
	public void testSqrt1000()
	{
		testSqrt(1000);
	}

	/**
	 * Tests boundary case sqrt(-1000)
	 * Failure: Expected result = <NaN>, actual result = 31.62.. 
	 */
	@Test
	public void testSqrtNeg1000()
	{
		testSqrt(-1000);
	}


	/**
	 * Tests random cases in (1,1000)
	 */
	@Test
	public void testSqrtOneTo1000() 
	{
		for(double a: randCases[2])
		{
			testSqrt(a);
		}
	}

	/**
	 * Tests random cases in (-1000,-1)
	 * Failure: Case -203, Expected result = <NaN>, actual result = 14.24.. 
	 */
	@Test
	public void testSqrtNegOneToNeg1000() 
	{
		for(double a: randCases[3])
		{
			testSqrt(a);
		}
	}

	/**
	 * Tests boundary case sqrt(MAX_DOUBLE)
	 */
	@Test
	public void testSqrtMax()
	{
		testSqrt(MAX_DOUBLE);
	}

	/**
	 * Tests boundary case sqrt(-MAX_DOUBLE)
	 * Failure: Expected result = <NaN>, actual result = 1.34E54
	 */
	@Test
	public void testSqrtNegMax()
	{
		testSqrt(-MAX_DOUBLE);
	}


	/**
	 * Tests random cases in (1000,MAX_DOUBLE)
	 */
	@Test
	public void testSqrt1000ToMax() 
	{
		for(double a: randCases[4])
		{
			testSqrt(a);
		}
	}

	/**
	 * Tests random cases in (-MAX_DOUBLE,-1000)
	 * Failure: Case -4.67E8, Expected result = <NaN>, actual result = 21626.13..
	 */
	@Test
	public void testSqrtNeg1000ToNegMax() 
	{
		for(double a: randCases[5])
		{
			testSqrt(a);
		}
	}

	/**
	 * Helper method that tests squaring of a number "a Square", implemented by FaultyCalculator by replacing the top element
	 * of the stack with its square.
	 * This is used in individual test cases to reduce redundant code.
	 * Test accurate to 4 d.p.
	 */
	private void testSquare(double a)
	{
		try
		{
			fc.enter(a);
			fc.square();
			double expect = a*a;
			double actual = fc.top();
			double delta = 0.0001;
			fc.clear();
			assertEquals("Correct values for square(" + a + ")", expect, actual, delta);
		}
		catch(CalculatorException ce)
		{
				fail("Calculator exception thrown: square(" + a + ")");
		}
	}

	/**
	 * Tests boundary case square(0)
	 */
	@Test
	public void testSquareZero()
	{
		testSquare(0);
	}

	/**
	 * Tests boundary case square(-1)
	 * Failure: Expected result = 1, actual result = -1 
	 */
	@Test
	public void testSquareNegOne()
	{
		testSquare(-1);
	}

	/**
	 * Tests boundary case square(1)
	 */
	@Test
	public void testSquareZeroOne()
	{
		testSquare(1);
	}

	/**
	 * Tests random cases in (0,1)
	 */
	@Test
	public void testSquareZeroToOne() 
	{
		for(double a: randCases[0])
		{
			testSquare(a);
		}
	}

	/**
	 * Tests random cases in (-1,0)
	 * Failure: Case -5.07.., Expected result = 0.25.., actual result = -0.25..
	 */
	@Test
	public void testSquareZeroToNegOne()
	{
		for(double a: randCases[1])
		{
			testSquare(a);
		}
	}

	/**
	 * Tests boundary case square(1000)
	 */
	@Test
	public void testSquare1000()
	{
		testSquare(1000);
	}

	/**
	 * Tests boundary case square(-1000)
	 * Failure: Expected result = 1000000, actual result = -1000000
	 */
	@Test
	public void testSquareNeg1000()
	{
		testSquare(-1000);
	}


	/**
	 * Tests random cases in (1,1000)
	 */
	@Test
	public void testSquareOneTo1000() 
	{
		for(double a: randCases[2])
		{
			testSquare(a);
		}
	}

	/**
	 * Tests random cases in (-1000,-1)
	 * Failure: Case -765, Expected result = 585225, actual result = -585225
	 */
	@Test
	public void testSquareNegOneToNeg1000() 
	{
		for(double a: randCases[3])
		{
			testSquare(a);
		}
	}

	/**
	 * Tests boundary case square(MAX_DOUBLE)
	 */
	@Test
	public void testSquareMax()
	{
		testSquare(MAX_DOUBLE);
	}

	/**
	 * Tests boundary case square(-MAX_DOUBLE)
	 * Expected result = <Infinity>, actual result = <-Infinity>
	 */
	@Test
	public void testSquareNegMax()
	{
		testSquare(-MAX_DOUBLE);
	}


	/**
	 * Tests random cases in (1000,MAX_DOUBLE)
	 */
	@Test
	public void testSquare1000ToMax() 
	{
		for(double a: randCases[4])
		{
			testSquare(a);
		}
	}

	/**
	 * Tests random cases in (-MAX_DOUBLE,-1000)
	 * Failure: Case -1.5E9, Expected result = 2.27E18, actual result = -2.27E18
	 */
	@Test
	public void testSquareNeg1000ToNegMax() 
	{
		for(double a: randCases[5])
		{
			testSquare(a);
		}
	}

	/**
	 * Not tested
	 */
	@Test
	public void testPi() 
	{
		fail("Not yet implemented");
	}

	/**
	 * Not tested
	 */
	@Test
	public void testAclear() 
	{
		fail("Not yet implemented");
	}

	/**
	 * Not tested
	 */
	@Test
	public void testArecall()
	{
		fail("Not yet implemented");
	}

	/**
	 * Not tested
	 */
	@Test
	public void testAplus() 
	{
		fail("Not yet implemented");
	}

	/**
	 * Not tested
	 */
	@Test
	public void testAstore() 
	{
		fail("Not yet implemented");
	}

}
